package com.santu.edu.supporter.interceptor;

import cn.hutool.core.util.StrUtil;
import com.santu.edu.supporter.exception.BizException;
import com.santu.edu.supporter.exception.RequestError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingRequestValueException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Objects;
import java.util.Optional;

/**
 * @author luoyaoqi
 * @createTime 2024/4/16
 * @description
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    public GlobalExceptionHandler() {
    }

    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public ResponseData<Object> handleMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        RequestError error = RequestError.METHOD_NOT_SUPPORT;
        String msg = StrUtil.format(error.getDesc(), new Object[]{e.getMethod()});
        return ResponseData.error(error.getCode(), msg);
    }

    @ExceptionHandler({MethodArgumentTypeMismatchException.class, MissingRequestValueException.class, HttpMediaTypeNotSupportedException.class, HttpMessageNotReadableException.class, ConstraintViolationException.class, MethodArgumentNotValidException.class, BindException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData<Object> handleBadRequestException(Exception e) {
        RequestError error = RequestError.PARAM_ERROR;
        String msg = error.getDesc();
        if (e instanceof MethodArgumentTypeMismatchException) {
            MethodArgumentTypeMismatchException ex = (MethodArgumentTypeMismatchException) e;
            msg = StrUtil.format("{}参数类型不匹配", new Object[]{ex.getName()});
        } else if (e instanceof MissingRequestValueException) {
            MissingRequestValueException ex = (MissingRequestValueException) e;
            error = RequestError.MissingRequestValue;
            msg = StrUtil.format(error.getDesc(), new Object[]{ex.getMessage()});
        } else if (e instanceof HttpMediaTypeNotSupportedException) {
            HttpMediaTypeNotSupportedException ex = (HttpMediaTypeNotSupportedException) e;
            error = RequestError.MEDIA_TYPE_NOT_SUPPORTED;
            MediaType mediaType = ex.getContentType();
            if (Objects.nonNull(mediaType)) {
                msg = StrUtil.format(error.getDesc(), new Object[]{mediaType.toString()});
            } else {
                msg = StrUtil.format(error.getDesc(), new Object[]{"UnKnow"});
            }
        } else if (e instanceof HttpMessageNotReadableException) {
            error = RequestError.PARAM_EMPTY_ERROR;
            msg = StrUtil.format(error.getDesc(), new Object[]{"请求参数"});
        } else if (e instanceof ConstraintViolationException) {
            ConstraintViolationException ex = (ConstraintViolationException) e;
            msg = ((ConstraintViolation) ex.getConstraintViolations().iterator().next()).getMessage();
        } else if (e instanceof MethodArgumentNotValidException) {
            msg = (String) Optional.of(((MethodArgumentNotValidException) e).getBindingResult()).map(Errors::getFieldError).map(DefaultMessageSourceResolvable::getDefaultMessage).orElse(error.getDesc());
        } else if (e instanceof BindException) {
            BindException ex = (BindException) e;
            FieldError fieldError = ex.getBindingResult().getFieldError();
            if (Objects.nonNull(fieldError)) {
                msg = fieldError.getDefaultMessage();
            }
        }

        return ResponseData.error(error.getCode(), msg);
    }

    @ExceptionHandler({BizException.class})
    @ResponseStatus(HttpStatus.OK)
    public ResponseData<Object> handleBusinessException(BizException e) {
        String errorMsg = e.getCustomMsg();
        if (StrUtil.isBlank(errorMsg)) {
            errorMsg = e.getError().getDesc();
        }

        return ResponseData.error(e.getError().getCode(), errorMsg);
    }

    @ExceptionHandler({Exception.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseData<Object> handleBusinessException(Exception e) {
        log.error("系统异常:", e);
        RequestError systemError;
        if (e instanceof NullPointerException) {
            systemError = RequestError.NULL_POINTER_ERROR;
        } else {
            systemError = RequestError.UNKNOWN_ERROR;
        }

        return ResponseData.error(systemError.getCode(), e.getLocalizedMessage());
    }

}
